package com.crc.luojian.util;

import com.crc.luojian.annotation.Autowired;
import com.crc.luojian.annotation.Component;
import com.crc.luojian.annotation.Transactional;
import com.crc.luojian.beans.model.ApplicationContext;
import com.crc.luojian.beans.model.BeanDefinition;
import com.crc.luojian.component.TransactionManager;
import com.crc.luojian.factory.impl.DefaultBeanFactory;
import net.sf.cglib.core.CollectionUtils;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @program: luojian-springframework
 * @description: 代理对象工具类
 * @author: luojian58
 * @create: 2021-06-10 15:38
 */

public class ProxyInstanceUtil implements MethodInterceptor{

    private TransactionManager transactionManager = new TransactionManager();
    public Object getProxy(Object obj) {
         Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(obj.getClass());
         enhancer.setCallback(this);
         Object proxObject = enhancer.create();
         populateBean(proxObject,obj);
         return proxObject;

    }

    private void populateBean(Object proxObject,Object target) {
        try {

            Field[] declaredFields = target.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                if (field.getAnnotation(Autowired.class) == null) {
                    continue;
                }
                field.setAccessible(true);
                Field field1 = target.getClass().getDeclaredField(field.getName());
                field1.setAccessible(true);
                field.set(proxObject, field1.get(target));

            }
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }


    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        try {
            transactionManager.beginTransaction();
            result = methodProxy.invokeSuper(object, args);
            transactionManager.commit();
        }catch (Exception e){
            if(method.getAnnotation(Transactional.class)!= null) {
                if (e.getClass().getGenericSuperclass() == RuntimeException.class) {
                    transactionManager.rollback();
                }
                List<Class<? extends Throwable>> rollbackFor = getRollbackFor(object, method);
                if (Objects.isNull(rollbackFor) && rollbackFor.size() > 0) {
                    for (Class<? extends Throwable> throwable : rollbackFor) {
                        if (e.getClass() == throwable) {
                            transactionManager.rollback();
                            break;
                        }
                    }
                }
            }
            throw e;
        }finally {
            transactionManager.releaseConnection();
        }

        return result;
    }
    /**
     * @Author luojian
     * @Description 获取rollback
     * @Date  2021/6/14 11:21
     * @Param [bean, method]
     * @return java.util.List<java.lang.Class<? extends java.lang.Throwable>>
     **/
    private List<Class<? extends Throwable>> getRollbackFor(Object bean, Method method){
        Transactional transactional = bean.getClass().getAnnotation(Transactional.class);
        List<Class<? extends Throwable>> rollback = new ArrayList<>();
        if(transactional != null){
            rollback.addAll(Arrays.asList(transactional.rollbackFor()));
        }
        Transactional annotation = method.getAnnotation(Transactional.class);
        if(Objects.isNull(annotation)){
            rollback.addAll(Arrays.asList(annotation.rollbackFor()));
        }
        return rollback;
    }
}
